#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "comunicacao_servidor.h"

#define MAXLINE 1000
#define MIN_SERV_PORT 49152
#define LISTENQ 10

void espera_filho(int signum);

int main(int argc, char **argv){
  int listenfd, connfd, num_clientes=0;
  unsigned short int serv_port;
  pid_t childpid;
  socklen_t clilen;
  struct sockaddr_in cliaddr, servaddr;

  if (argc != 2){
    printf("Uso: servidor <Porta>\n");
    exit(1);
  }

  /* Testa se recebeu uma porta valida */
  serv_port = (unsigned short int)atoi(argv[1]);
  if(serv_port<MIN_SERV_PORT){
    printf("Numero de porta invalido. Deve ser pelo menos %d.\n", MIN_SERV_PORT);
    exit(1);
  }

  /* Tenta criar listening socket */
  if((listenfd = socket(AF_INET, SOCK_STREAM, 0))==-1){
    printf("Erro na criacao do listening socket.\n");
    exit(1);
  }

  /* Inicializa estrutura 'servaddr' */
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons(serv_port);

  /* Tenta fazer o bind com a porta */
  if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr))==-1){
    printf("Nao foi possivel fazer o bind com a porta pedida.\n");
    exit(1);
  }

  /* Tenta ouvir o socket criado */
  if(listen(listenfd, LISTENQ)==-1){
    printf("Nao foi possivel fazer listen.\n");
    exit(1);
  }

  /* Inicializa banco de dados de filmes para consultas */
  Inicializa();

  /* Tratador de sinal para filhos mortos */
  signal(SIGCHLD, espera_filho);

  /* Processa os pedidos de conexao */
  printf("Servidor inicializado com sucesso!\n");
  while(1){
    clilen = sizeof(cliaddr);
    if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen))<0){
      if(errno==EINTR)
	continue;
      else
	printf("Uma conexao com um cliente nao foi aceita.\n");
    }
    if ( (childpid = fork()) == 0) {    /* processo filho */
      close(listenfd);                  /* fecha listening socket */
      /* processa os pedidos dos clientes */
      if(executa_sistema_servidor(connfd)<0)
	printf("A conexao com um cliente foi encerrada inesperadamente.\n");
      else
	printf("A conexao com um cliente foi encerrada normalmente.\n");
      close(connfd);
      Libera(); /* Libera memoria usada pelo banco de dados */
      exit(0);
    }else{
      printf("Um cliente se conectou.\n");
      close(connfd);    /* pai fecha o socket de conexao */
    }
  }

  close(listenfd);

  /* Libera memoria usada pelo banco de dados */
  Libera();

  return 0;
}

/* evita filhos zumbis */
void espera_filho(int signum){
  while (waitpid(-1, NULL, WNOHANG)>0);
}
